﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace GN.Pay.Utils.Security
{
    /// <summary>
    /// Rsa 帮助
    /// </summary>
    public static class RsaUtils
    {
        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="xmlPublicKey">公钥</param>
        /// <param name="sourceString">源数据</param>
        public static string Encrypt(string xmlPublicKey, string sourceString)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.FromXmlString(xmlPublicKey);
                byte[] bytes = Encoding.UTF8.GetBytes(sourceString);
                return Encrypt(rsa, bytes);
            }
        }

        /// <summary>
        /// RSK 加密长度
        /// </summary>
        public const int RSA_ENCRYPT_SIZE = 86;

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string Encrypt(RSACryptoServiceProvider rsa, byte[] data)
        {
            int size = rsa.KeySize / 8; //8位一个字节
            using (SHA1 sha = new SHA1CryptoServiceProvider())
            {
                StringBuilder result = new StringBuilder();
                int length = data.Length;
                int page = length / RSA_ENCRYPT_SIZE;
                if (data.Length % RSA_ENCRYPT_SIZE != 0)
                {
                    page++;
                }
                byte[] bytes = new byte[page * size];
                int count = length;
                int index = 0;
                for (int j = 0; j < page; j++)
                {
                    int byteSize = count >= RSA_ENCRYPT_SIZE ? RSA_ENCRYPT_SIZE : count;
                    byte[] buf = new byte[byteSize];
                    for (int i = 0; i < byteSize; i++)
                    {
                        buf[i] = data[index];
                        index++;
                        count--;
                    }
                    byte[] source = rsa.Encrypt(buf, true);
                    Array.Copy(source, 0, bytes, j * source.Length, source.Length);
                }
                return Convert.ToBase64String(bytes);
            }

        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="xmlPrivateKey">私钥</param>
        /// <param name="encryptedString">待解密的数据</param>
        /// <returns>解密后的结果</returns>
        public static string Decrypt(string xmlPrivateKey, string encryptedString)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                rsa.FromXmlString(xmlPrivateKey);
                byte[] rgb = Convert.FromBase64String(encryptedString);
                return Decrypt(rsa, rgb, Encoding.UTF8);
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="rsa"></param>
        /// <param name="data"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string Decrypt(RSACryptoServiceProvider rsa, byte[] data, Encoding encoding)
        {
            int size = rsa.KeySize / 8; //8位一个字节
            using (SHA1 sha = new SHA1CryptoServiceProvider())
            {
                StringBuilder result = new StringBuilder();
                for (int j = 0; j < data.Length / size; j++)
                {
                    byte[] buf = new byte[size];
                    for (int i = 0; i < size; i++)
                    {
                        buf[i] = data[i + size * j];
                    }
                    byte[] source = rsa.Decrypt(buf, true);
                    result.Append(encoding.GetString(source));
                }
                return result.ToString();
            }
        }

        /// <summary>
        /// 创建证书
        /// </summary>
        /// <param name="rawData">二进制</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public static X509Certificate2 CreateCertificate(byte[] rawData, string password)
        {
            ExceptionUtils.CheckNotNull(rawData, nameof(rawData));
            if (password == null)
            {
                password = "";
            }
            return new X509Certificate2(rawData, password,
                X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
        }

        /// <summary>
        /// 获取私钥提供
        /// </summary>
        /// <param name="rawData">二进制数据</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public static RSACryptoServiceProvider PrivateKeyProvider(byte[] rawData, string password)
        {            
            var certificate = CreateCertificate(rawData, password);
            if (certificate.PrivateKey == null)
            {
                throw new ArgumentException("私钥数据格式不正确。");
            }
            return (RSACryptoServiceProvider)certificate.PrivateKey;
        }

        /// <summary>
        /// 获取证书的序列号
        /// </summary>
        /// <param name="rawData">二进制数据</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public static string CertSerialNumber(byte[] rawData, string password)
        {
            var certificate = CreateCertificate(rawData, password);
            return BigInteger.Parse(certificate.SerialNumber, NumberStyles.HexNumber).ToString();
        }


        /// <summary>
        /// 获取私钥提供
        /// </summary>
        /// <param name="rawData">二进制数据</param>
        /// <returns></returns>
        public static RSACryptoServiceProvider PublicKeyProvider(byte[] rawData)
        {
            ExceptionUtils.CheckNotNull(rawData, nameof(rawData));
            var certificate = CreateCertificate(rawData, "");
            if (certificate.PublicKey == null || certificate.PublicKey.Key == null)
            {
                throw new ArgumentException("公钥数据格式不正确。");
            }
            return (RSACryptoServiceProvider)certificate.PublicKey.Key;
        }



    }
}
